#include "RBQtApplication.h"

#include <RB.h>
#include <RBApplication/RBApplication.h>
#include <RBMain/RBDatabaseQuery.h>
#include <RBMain/RBSceneNode.h>
#include <RBMain/RBCamera.h>
#include <RBMain/RBTechniqueFactory.h>
#include <RBMain/RBTechniqueShader.h>

#include <QTGUI/QPushButton>
#include <QTCore/QSignalMapper>

RBQtApplication::RBQtApplication(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{
	ui.setupUi(this);
    m_qtTimer.setInterval(10);
	QObject::connect(ui.actionQuit, SIGNAL(activated()), this, SLOT(close()));
    QObject::connect(&m_qtTimer, SIGNAL(timeout()), this, SLOT(OnIdle()));
    m_qtTimer.start();
    m_pSignalMapper = 0;
    QObject::connect(&m_qtTimer, SIGNAL(timeout()), this, SLOT(OnIdle()));

}

RBQtApplication::~RBQtApplication()
{
    RBApplication::GetSingleton()->Close();
    delete m_pSignalMapper;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnFileLoad()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open DB", QString(), tr("DB Files (*.dae *.rbs)"));
    if (QFile::exists(fileName))
    {
        RBApplication::GetSingleton()->Close();
        if (RBApplication::GetSingleton()->Open(fileName.toStdString()))
        {
            RBCamera* pCamera = (RBCamera*)RBApplication::GetSingleton()->GetSceneRoot()->GetObjectByName("SunCamera");

            RBRenderPass* pMRTShadow = RBApplication::GetSingleton()->GetRenderProcess()->GetPassByName("ShadowMRT");
            if (pMRTShadow)
            {
                pMRTShadow->Culler->SetCamera(pCamera);
            }
            PopulateTreeView(RBApplication::GetSingleton()->GetSceneRoot());
        }
    }   
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnFileSave()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save DB", QString(), tr("DB Files (*.rbs)"));
    RBApplication::GetSingleton()->Save(fileName.toStdString());
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnIdle()
{
    ui.RenderView->repaint();
}

class CTreePopulator : public RBSceneVisitor
{
public:
    CTreePopulator(RBQtApplication* pApp) : RBSceneVisitor(VT_DEPTHFIRST), m_pApp (pApp) {}

    virtual EVisitorCode::ContinueType    Visit           (RBSceneObject* p)
    {
        m_pApp->AddEntityToTreeItem(p, m_vQNodes.empty() ? NULL : m_vQNodes.back());
        return EVisitorCode::CT_CONTINUE;
    }

    virtual EVisitorCode::ContinueType    Visit           (RBSceneNode* p)
    {
        QTreeWidgetItem* pCurrentItem = m_vQNodes.empty() ? NULL : m_vQNodes.back();
        QTreeWidgetItem* pNewItem = m_pApp->AddEntityToTreeItem(p, pCurrentItem);
        m_vQNodes.push_back(pNewItem);
        EVisitorCode::ContinueType eCT = RBSceneVisitor::VisitChildren(p);
        m_vQNodes.pop_back();
        return eCT;
    }

    virtual EVisitorCode::ContinueType    Visit           (RBGeometry* p)
    {
        p->SetTechnique(RBTechniqueFactory::GetSingleton()->Retrieve("Error"));
        return Visit((RBGeometry::Super*) p);
    }

    std::vector<QTreeWidgetItem*> m_vQNodes;
    RBQtApplication* m_pApp;
};

template<class RBTEntity> 
void AddItems(QTreeWidget* pSceneGraphTree, const char* pcName, RBSceneDatabase* pDB, RBQtApplication* pApp)
{
    QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(pcName));
    pSceneGraphTree->addTopLevelItem(item);
    RBTEntityLibrary<RBTEntity>* pLib = pDB->GetLibrary<RBTEntity>();
    for (uint32 ui = 0; ui < pLib->GetEntityCount(); ++ui)
    {
        pApp->AddEntityToTreeItem(pLib->GetEntity(ui), item);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::PopulateTreeView(RBSceneNode* pRoot)
{
    // clear everything
    while (ui.SceneGraphTree->takeTopLevelItem(0)) {}

    CTreePopulator kPopulator(this);
    pRoot->Accept(kPopulator);
}

QTreeWidgetItem* RBQtApplication::AddEntityToTreeItem( RBSceneObject* pObject, QTreeWidgetItem* parent )
{
    QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(RBStringTools::FormatString("'%s' (%s)", 
                                                            pObject->GetName().c_str(), pObject->GetRTTI()->GetName().c_str()).c_str()));
    m_TreeToObjects[item] = pObject;
    if (parent)
    {
        parent->addChild(item);
    }
    else
    {
        ui.SceneGraphTree->addTopLevelItem(item);
    }
    return item;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnUpdateMaterial( const QModelIndex& index )
{
    QTreeWidgetItem* item = (QTreeWidgetItem*)index.internalPointer();
    RBSceneObject* pObject = m_TreeToObjects[item];
    if (pObject)
    {
        RBApplication::GetSingleton()->OnObjectSelected(pObject);
        if (RbIsKindOf(RBGeometry, pObject))
        {
            m_pEditableGeometry = static_cast<RBGeometry*>(pObject);
            UpdateMaterialWnd();
        }
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnLoadTexture(int iSampler)
{
    if (m_pEditableGeometry && m_pEditableGeometry->GetMaterial())
    {
        QString fileName = QFileDialog::getOpenFileName(this, "Open Image", QString(), tr("Image Files (*.png *.jpg *.bmp)"));
        if (QFile::exists(fileName))
        {
            m_pEditableGeometry->GetMaterial()->GetSampler(iSampler).Texture = 
                RBRenderer::GetSingleton()->GetHBMgr()->CreateTexture(fileName.toStdString().c_str());
        }
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnReloadShaders()
{
    ui.RenderView->GetRBApp()->GetRenderer()->RestoreLostDevice(true);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::UpdateMaterialWnd()
{
    if (m_pEditableGeometry)
    {
        RBTechniqueShader* pTechnique = RBDynamicCast(RBTechniqueShader, m_pEditableGeometry->GetTechnique());
        if (pTechnique)
        {
            QLayoutItem *child;
            while ((child = ui.MaterialParametersLayout->takeAt(0)) != 0)
            {
                delete child->layout();
                delete child->widget();
                delete child->spacerItem();
                delete child;
            }

            delete m_pSignalMapper;
            m_pSignalMapper = new QSignalMapper();

            const RBTechniqueShader::RBTechniqueParameterMap& map = pTechnique->GetParameterMap();
            for (RBTechniqueShader::RBTechniqueParameterMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
            {
                AddTechniqueParameter(iter->second);
            }
            connect(m_pSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnLoadTexture(int)));
        }
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::AddTechniqueParameter( const RBTechniqueParameter* pParam )
{
    if (pParam && pParam->GetSemantic() == RBTechniqueParameter::TPS_ATTRIBUTE)
    {
        QWidget* pParamContainer = new QWidget(ui.MaterialParameters);
        QBoxLayout* pBoxLayout = new QHBoxLayout(pParamContainer);
        // Label for parameter name
        {
            pBoxLayout->addWidget(new QLabel(QString(pParam->GetName().c_str()), pParamContainer), 0, Qt::AlignLeft);
        }
        // Value button created based on paramter type
        {
            if (pParam->GetType() == RBTechniqueParameter::TPT_TEXTURE)
            {
                QPushButton* pBrowseButton = new QPushButton(QString("Browse"), pParamContainer);
                QLabel* pFilename = new QLabel(QString(""), pParamContainer);
                pBoxLayout->addWidget(pFilename, 0, Qt::AlignRight);
                pBoxLayout->addWidget(pBrowseButton, 0, Qt::AlignRight);

                m_pSignalMapper->setMapping(pBrowseButton, (int)pParam->GetSamplerType());
                QObject::connect(pBrowseButton, SIGNAL(clicked()), m_pSignalMapper, SLOT (map()));
            }
        }

        ui.MaterialParametersLayout->addWidget(pParamContainer);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBQtApplication::OnLightCreate()
{
    RBLight* pLight = new RBLight;
    RBApplication::GetSingleton()->GetSceneRoot()->AddChild(pLight);
    PopulateTreeView(RBApplication::GetSingleton()->GetSceneRoot());
}